fix(api): a request that names a page or a size gets one - #1156
Merged
Conversation
GET /users?size=500 answered with every user. Spring's resolver reads its fallback unless the request carries both `page` and `size`, and this api's fallback is unpaged, so naming one half of a page silently bought nothing. A caller that names a size is one that cannot hold an unbounded answer, and it was handed exactly that. Measured on four users, before: no parameters, `?size=1` and `?page=0` all answered with four rows; only `?page=0&size=1` paged. After: the two halves each page, with 0 and the documented 20 filling in for whichever was not named, and a request naming neither still answers unpaged, which is what the listing is for. The two callers that named a size wanted the whole listing and were getting it by accident: loadMemberAccounts and UserPicker both filter what they hold, so they now ask for the listing and name no size. Left as they were, this change would have truncated them at 500 — the bug #1139 has just been fixed. Closes #1145
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1145.
Why
GET /users?size=500answered with every user. Not 500 — every one.Spring's
PageableHandlerMethodArgumentResolverreads its fallback unless the request carries bothpageandsize, andPagingConfigsets that fallback to unpaged. So naming one half of a page bought nothing, silently: the response is well formed and merely larger than the caller believed possible. A caller that names a size is one that cannot hold an unbounded answer.Measured against four users, before this change:
page.sizeGET /usersGET /users?size=1GET /users?page=0GET /users?page=0&size=1pagealone was ignored too, which the issue did not know.What this achieves
Either half means paged.
?size=1is one row;?page=0is the documented default of 20. Naming neither still answers unpaged, because a listing that nobody paged is what several readers want and whatunpagedByDefaultis for.How
PagingConfignow registers a resolver ahead of Spring Data's, overriding the one decision that was wrong: with neither parameter present it answers unpaged as before, and with either present it fills in0or20for the half that was not named and pages as asked.20is not invented — it is what the OpenAPI document already claimssizedefaults to, so the spec and the api now agree where before the spec described a parameter that was not honoured.The callers, which had to move with it
Both remaining callers that named a size wanted the whole listing and were getting it by accident. Left alone, this change would have truncated them at 500 — which is exactly the bug #1139 fixed a day ago, re-created from the other side.
loadMemberAccountsandUserPickerboth hold the list and filter it in the browser, so they now ask for the listing and name no size. Their behaviour on the wire is unchanged; what changed is that they say what they mean.Worth a reviewer's attention
That both of them fetch an unbounded listing at all is a real problem — the committee picker stopped doing it in #1139 by asking the api as the reader types. These two are rarer paths (attaching a roster entry, a board membership), which is the reasoning recorded in
users.ts, but the same argument that beat 500 will eventually beat "everybody". Out of scope here: this change is about the api keeping the promise its parameters make.Verification
./gradlew :services:api:test :services:api:integrationTest— both suites pass in full.Three new
UserControllerITcases pin all the shapes, and two of them were red first, for the right reasons:expected:<1> but was:<4>for a size without a page, andpage.size expected:<20> but was:<4>for a page without a size.yarn vitest run— 179 files, 1540 tests. One existing test asserted{query: {size: 500}}and now states the intent instead: it asks for the whole listing rather than a page whose size it would have to guess.yarn typecheckandyarn lintclean, and the generated spec does not drift.Diff breakdown —
█added░removed, scaled to the largest row.